﻿using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using MassIntellect.WorkIt;

namespace MassIntellect
{
	public class XmlSwitch<T, P, A> : XmlSwitch<T, P>
		where T : IXmlSelector, new()
		where P : IXmlPipeSelector, new()
		where A : IXmlArgNameSelector, new()
	{
		protected IXmlArgNameSelector XmlArgNameSelector;

		protected IXmlArgNameSelector GetArgNameSelector()
		{
			return XmlArgNameSelector ?? (XmlArgNameSelector = new A { Owner = this });
		}

		protected class SwitchMatrix
		{
			public PulseArgs Original { get; set; }

			public Dictionary<IPipe, PulseArgs> routes = new Dictionary<IPipe, PulseArgs>();

			public void Push(XElement xe, IPipe pipe, string line)
			{
				var route = routes.GetOrAddDefault(pipe, () => Original.Clone());
				route[line] = xe;
			}			
		}

		protected override void protectedPulse(PulseArgs args)
		{
			XElement xe;
			PulseArgs a2;
			IPipe pipe;
			string cntkey = GetContentKey();
			var sm = new SwitchMatrix { Original = args };
			if (args.ContainsKey(cntkey)
				&& (xe = args[cntkey] as XElement) != null)
			{
				GetXmlSelector();
				GetPipeSelector();
				GetArgNameSelector();
				if (MergeSignal)
					SendMerge(xe, args);
				else
					SendDiff(xe, args);
			}
		}

		private void SendMerge(XElement xe, PulseArgs original)
		{
			IPipe pipe;
			var sm = new SwitchMatrix { Original = original };
			foreach (var item in XmlSelector.Select(xe))
			{
				Echo("Селектор Xml {0} > {1}", xe.Name.LocalName, item.Name.LocalName);
				pipe = PipeSelector.Select(item);
				Echo("Коммутатор Xml {0} > {1}", item.Name.LocalName, pipe.Name);
				sm.Push(item, pipe, XmlArgNameSelector.Select(item));
			}
			foreach (var route in sm.routes)
				if (route.Key != this)
					route.Key.Pulse(route.Value);
				else
					ThroughPulse(route.Value);
		}

		private void SendDiff(XElement xe, PulseArgs original)
		{
			IPipe pipe;
			PulseArgs a2;			
			foreach (var item in XmlSelector.Select(xe))
			{
				a2 = original.Clone();
				Echo("Селектор Xml {0} > {1}", xe.Name.LocalName, item.Name.LocalName);
				pipe = PipeSelector.Select(item);
				Echo("Коммутатор Xml {0} > {1}", item.Name.LocalName, pipe.Name);
				a2[XmlArgNameSelector.Select(item)] = item;				
				if (pipe != this)
					pipe.Pulse(a2);
				else
					ThroughPulse(a2);
			}							
		}

		public override void SetUp(XElement xe)
		{
			base.SetUp(xe);
			XElement sx;
			if ((sx = xe.Element("XmlArgNameSelector")) != null)
			{
				GetArgNameSelector();
				XmlSerializeProvider.SetUp(XmlArgNameSelector, sx);
			}
		}

		public override XElement Serialize()
		{
			var sx = base.Serialize();
			if (XmlArgNameSelector != null)
				sx.Add(XmlSerializeProvider.Serialize(XmlArgNameSelector, "XmlArgNameSelector"));
			return sx;
		}
	}
}